package com.googlecode.flex_solder.controls  {

  /**
   * $Id: FromToTimeCombo.as 16 2009-01-03 03:38:29Z fuller.sethm $
   *
   * Author: Seth M. Fuller
   * Copyright: 2008
   *
   *  Permission is hereby granted, free of charge, to any person
   *  obtaining a copy of this software and associated documentation
   *  files (the "Software"), to deal in the Software without
   *  restriction, including without limitation the rights to use,
   *  copy, modify, merge, publish, distribute, sublicense, and/or
   *  sell copies of the Software, and to permit persons to whom the
   *  Software is furnished to do so, subject to the following
   *  conditions:
   *
   *  The above copyright notice and this permission notice shall be
   *  included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   *  OTHER DEALINGS IN THE SOFTWARE.
   *
   * This file is licensed under the BSD (revised) open source
   * license.
   *
   * This is part of the flex-solder components hosted at
   * flex-solder.googlecode.com.
   *
   *======================================================================
   *
   * Contains third-party source code released with the same license terms
   * that appear above with the following copyright notice:
   *
   * Copyright (c) 2008 Yahoo! Inc.  All rights reserved.  The copyrights
   * embodied in the content of this file are licensed under the BSD
   * (revised) open source license
   *
   */

  import flash.display.DisplayObject;
  import flash.events.Event;
  import flash.events.FocusEvent;
  import flash.events.KeyboardEvent;
  import flash.ui.Keyboard;
  import flash.text.TextLineMetrics;
	
  import mx.core.UITextField;
  import mx.core.IUIComponent;
  import mx.core.EdgeMetrics;
  import mx.core.IBorder;
  import mx.core.IFlexDisplayObject;
  import mx.core.IInvalidating;
  import mx.core.UIComponent;
  import mx.core.Container;

  import mx.containers.utilityClasses.Flex;

  import mx.controls.Label;
  import mx.controls.ComboBox;
  import mx.events.FlexEvent;
  import mx.managers.IFocusManagerContainer;
  import mx.styles.CSSStyleDeclaration;
  import mx.styles.ISimpleStyleClient;
  import mx.styles.StyleManager;
	
  /**
   * Number of pixels between the component's bottom border
   * and the bottom edge of its content area.
   *
   * @default 0
   */
  [Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
	
    /**
     * Number of pixels between the component's top border
     * and the top edge of its content area.
     *  
     * @default 0
     */
    [Style(name="paddingTop", type="Number", format="Length", inherit="no")]

    /**
     *  Number of pixels between children in the horizontal direction.
     *
     *  @default 0
     */
    [Style(name="horizontalGap", type="Number", format="Length", inherit="no")]

    //--------------------------------------
    //  Other Metadata
    //--------------------------------------

    [DefaultTriggerEvent("change")]

  public class FromToTimeCombo extends Container
    implements IFocusManagerContainer {
    protected const linePad:Number = 3;
    private var layoutStyleChanged:Boolean = false;

    /**
     * The internal subcontrol that draws the border and background.
     */
    protected var border:IFlexDisplayObject;

    private var fromToWidth:Number = 0;
    private var fromToHeight:Number = 0;

    private var _fromTimeCombo:TimeCombo = null;
    private var _toTimeCombo:TimeCombo = null;

    private var _fromLabel:Label = null;
    private var _toLabel:Label = null;
    private var _duration:Number = -1;

    /**
     * @private
     * Storage for the toTimeValue property.
     */
    protected var _toTimeValue:Date = null;
    protected var _fromTimeValue:Date = null;

    public static var fromText:String = "From";
    public static var toText:String = "To";

    public static const heightPad:Number = 5;

    public static var timeComboWidth:Number = 0;
    public static var timeComboHeight:Number = 0;

    public static var labelWidth:Number = 0;
    public static var labelWidthMT:Number = 0;
    public static var labelPctWidth:Number = 20;

    public static var labelHeight:Number = 0;
    public static var labelHeightMT:Number = 0;

    /**
     * @private
     * Flag that indicates that the enabled property has changed.
     */
    protected var enabledChanged:Boolean = false;
		
    [Inspectable(category="General", enumeration="true,false", defaultValue="true")]
      /**
       * @private
       * Disable ComboBox when we're disabled.
       */
      override public function set enabled(value:Boolean):void
    {
      if(value == this.enabled)
	{
	  return;
	}
		
      super.enabled = value;
      this.enabledChanged = true;
		
      this.invalidateProperties();
		
      if(this.border && this.border is IInvalidating)
	{
	  IInvalidating(this.border).invalidateDisplayList();
	}
    }
		
    /**
     * @private
     * Storage for the editable property.
     */
    private var _editable:Boolean = true;
		
    /**
     * @private
     * Flag that indicates that the editable property has changed.
     */
    protected var editableChanged:Boolean = false;
		
    [Bindable("editableChanged")]
      [Inspectable(category="General", defaultValue="true")]
      /**
       * Indicates whether the user is allowed to edit the text in this control.
       * If <code>true</code>, the user can edit the text.
       *
       * @default true
       * 
       * @tiptext Specifies whether the component is editable or not
       * @helpid 3196
       */
      public function get editable():Boolean
      {
	return this._editable;
      }
		
    //--------------------------------------
    //  Static Methods
    //--------------------------------------
	
    /**
     * @private
     * Sets the default style values for this control.
     */
    public static function initializeStyles():void
    {
      var styleDeclaration:CSSStyleDeclaration
	= StyleManager.getStyleDeclaration("FromToCombo");
      if(!styleDeclaration)
	{
	  styleDeclaration = new CSSStyleDeclaration();
	}
			
      styleDeclaration.defaultFactory = function():void
      {
// 	this.backgroundColor = 0xffffff;
// 	this.backgroundDisabledColor = 0xdddddd;

	//other styles are based on the framework defaults
      };
			
      StyleManager.setStyleDeclaration("FromToCombo", 
				       styleDeclaration, false);
    }
    initializeStyles();
		
    public function FromToTimeCombo() {
      super();

    }

    /**
     * @private
     */
    override public function styleChanged(styleProp:String):void {
      super.styleChanged(styleProp);
			
      if(!styleProp || styleProp.indexOf("font") >= 0 
	 || styleProp.indexOf("text") >= 0
	|| StyleManager.isSizeInvalidatingStyle(styleProp)) {
	layoutStyleChanged = true;

	initWidthValues(true);

	this.invalidateSize();
	this.invalidateDisplayList();
      }
    }

    protected function initWidthValues(forceRecalc:Boolean = false):void {
      if (labelWidth > 0 && !forceRecalc) {

 	return;
      }

      //we have to do this even if we have explicit dimensions
      //because positioning relies on the size of two characters

      var tempTextField:UITextField = new UITextField();
      tempTextField.styleName = this.styleName;
      tempTextField.ignorePadding = true;
      tempTextField.text = fromText;
      
      //figured invalidation and validation would be enough
      //but we have to add and remove the UITextField too!
      this.addChild(tempTextField);
				
      labelWidth = tempTextField.measuredWidth;
      labelHeight = tempTextField.measuredHeight;

      this.removeChild(tempTextField);


    }

    //--------------------------------------
    //  Protected Methods
    //--------------------------------------
	
    /**
     * @private
     */
    override protected function createChildren():void {
      super.createChildren();
		
      _fromLabel = new Label();
      _fromLabel.text = fromText;
      this.addChild(_fromLabel);

      _fromTimeCombo = new TimeCombo();
      this.addChild(_fromTimeCombo);
      _fromTimeCombo.validateNow();

      _toLabel = new Label();
      _toLabel.text = toText;
      this.addChild(_toLabel);

      _toTimeCombo = new TimeCombo();
      this.addChild(_toTimeCombo);
      _toTimeCombo.validateNow();
    }
		
    /**
     * @private
     */
    override protected function commitProperties():void {
      super.commitProperties();
			
      if(this.enabledChanged || this.editableChanged) {

	if(this.enabledChanged) {
	  _fromTimeCombo.enabled = this.enabled;
	}
			
	if(this.enabledChanged) {
	  _toTimeCombo.enabled = this.enabled;
	}

	this.enabledChanged = false;
	this.editableChanged = false;
      }
    }
		
    override protected function measure():void {
      super.measure();

      if (fromToWidth && !layoutStyleChanged) {

	measuredMinWidth = fromToWidth;
	measuredMinHeight = fromToHeight;
	measuredWidth = fromToWidth;
	measuredHeight = fromToHeight;
	return;
      }

      initWidthValues();
      layoutStyleChanged = false;

      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");
			
      if (this.border) {
	var metrics:EdgeMetrics = (this.border is IBorder 
				   ? IBorder(this.border).borderMetrics
				   : EdgeMetrics.EMPTY);
	this.measuredWidth = metrics.left + metrics.right;
	this.measuredHeight = metrics.top + metrics.bottom;
      }
			
      //hours and minutes are always displayed
      this.measuredWidth += calcFullWidth();

      this.measuredHeight += 
	((_fromTimeCombo.getComboHeight() + BaseMultiCombo.comboHeightPad) * 2);
//       this.measuredHeight += heightPad;

      //since we don't want the text being cut off, the min width and height are the same as the regular values.
      this.measuredMinWidth = this.measuredWidth;
      this.measuredMinHeight = this.measuredHeight;
			
      fromToWidth = this.measuredWidth;
      fromToHeight = this.measuredHeight;
    }

    /**
     * @private
     */
    override protected function
      updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
			
      if (this.border != null) {
	this.border.setActualSize(unscaledWidth, unscaledHeight);
      }

      this.layoutComboBoxes();

      super.updateDisplayList(unscaledWidth, unscaledHeight);

    }

    /**
     * @private
     * Overrides default layout algorithm to include the separator fields. 
     */
    protected function layoutComboBoxes():void {

      var metrics:EdgeMetrics = (this.border && this.border is IBorder
				 ? IBorder(this.border).borderMetrics
				 : EdgeMetrics.EMPTY);

      var xPosition:Number = metrics.left;
      var yPosition:Number = metrics.top;

      timeComboHeight = _fromTimeCombo.getComboHeight();
      timeComboWidth = _fromTimeCombo.getComponentWidths();

      _fromLabel.x = xPosition;
      _fromLabel.y = yPosition;
      _fromLabel.text = fromText;
      _fromLabel.visible = true;
       _fromLabel.minWidth = labelWidth;
       _fromLabel.width = labelWidth;

       _fromLabel.height = timeComboHeight;
      xPosition += _fromLabel.width;


      _fromTimeCombo.x = xPosition;
      _fromTimeCombo.y = yPosition;

//       var fromWidth:Number = _fromTimeCombo.getComponentWidths();

      _fromTimeCombo.setActualSize(timeComboWidth, timeComboHeight);


      xPosition = metrics.left;
      yPosition = metrics.top + _fromTimeCombo.getComboHeight() + linePad;

      _toLabel.x = xPosition;
      _toLabel.y = yPosition;
      _toLabel.text = toText;
      _toLabel.visible = true;
       _toLabel.minWidth = labelWidth;
       _toLabel.width = labelWidth;

       _toLabel.height = timeComboHeight;
      xPosition += _toLabel.width;

      _toTimeCombo.x = xPosition;
      _toTimeCombo.y = yPosition;
      _toTimeCombo.setActualSize(timeComboWidth,  timeComboHeight);

    }

    protected function calcFullWidth():Number {
      var calcWidth:Number = 0;
      var gap:Number = getStyle("horizontalGap");
      if (numChildren == 0) {
	return 0;
      }
      var toWidth:Number = 0;
      var fromWidth:Number = 0;
      toWidth = _toLabel.measuredWidth + gap + _toTimeCombo.measuredWidth;

      fromWidth = _fromLabel.measuredWidth + gap + _fromTimeCombo.measuredWidth;

      if (toWidth > fromWidth) {
	calcWidth = toWidth;
      }
      else {
	calcWidth = fromWidth;
      }

      return calcWidth;
    }
		
    public function get toTimeValue():Date {
      _toTimeValue = _toTimeCombo.timeValue;
      return _toTimeValue;
    }

    public function set toTimeValue(toTime:Date):void {
      _toTimeValue = toTime;

      _toTimeCombo.timeValue = _toTimeValue;

    }

    public function get fromTimeValue():Date {
      _fromTimeValue = _fromTimeCombo.timeValue;

      return _fromTimeValue;
    }

    public function set fromTimeValue(fromTime:Date):void {
      this._fromTimeValue = fromTime;

      _fromTimeCombo.timeValue = _fromTimeValue;

    }

  }
}